using System;
using System.Collections.Generic;
using System.IO;
using EasyStorage;
using EventFlowLibrary;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using EventFlowLibrary.LevelComponents;
using EventFlowLibrary.GameStructure;
using Microsoft.Xna.Framework.GamerServices;
using System.Globalization;

namespace ScreenManagement
{
    /// <summary>
    /// The pause menu comes up over the top of the game,
    /// giving the player options to resume or quit.
    /// </summary>
    class PauseMenuScreen : MenuScreen
    {
        #region Fields

        World world;

        #endregion

        #region Initialization


        /// <summary>
        /// Constructor.
        /// </summary>
        public PauseMenuScreen(World _world)
            : base("Paused")
        {
            world = _world;
            world.levelManager.journal.IsShown = false;

            // Add the Resume Game menu entry.
            MenuEntry resumeGameMenuEntry = new MenuEntry("Resume Game");
            resumeGameMenuEntry.Selected += OnCancel;
            MenuEntries.Add(resumeGameMenuEntry);

            // Add save game entry
            MenuEntry SaveGameMenuEntry = new MenuEntry("Save Game");
            SaveGameMenuEntry.Selected += SaveGameMenuEntry_Selected;
            MenuEntries.Add(SaveGameMenuEntry);

            // Add options entry
            MenuEntry OptionsMenuEntry = new MenuEntry("Options");
            OptionsMenuEntry.Selected += OptionsMenuEntry_Selected;
            MenuEntries.Add(OptionsMenuEntry);

            // If this is a single player game, add the Quit menu entry.
            MenuEntry quitGameMenuEntry = new MenuEntry("Quit Game");
            quitGameMenuEntry.Selected += QuitGameMenuEntrySelected;
            MenuEntries.Add(quitGameMenuEntry);
        }

        public override void LoadContent()
        {
            base.LoadContent();

            this.ScreenManager.SaveDevice.SaveCompleted -= SaveDevice_SaveCompleted;
            this.ScreenManager.SaveDevice.SaveCompleted += new SaveCompletedEventHandler(SaveDevice_SaveCompleted);
        }

        #endregion

        #region Handle Input


        /// <summary>
        /// Event handler for when the Quit Game menu entry is selected.
        /// </summary>
        void QuitGameMenuEntrySelected(object sender, PlayerIndexEventArgs e)
        {
            MessageBoxScreen confirmQuitMessageBox = new MessageBoxScreen(new string[] { "Are you sure you want to quit this game?" }, true, true, true, true);

            confirmQuitMessageBox.Accepted += ConfirmQuitMessageBoxAccepted;

            ScreenManager.AddScreen(confirmQuitMessageBox, ControllingPlayer);
        }

        void OptionsMenuEntry_Selected(object sender, PlayerIndexEventArgs e)
        {
            ScreenManager.AddScreen(new OptionsMenuScreen(this.world.levelManager.FPSCamera), ControllingPlayer);
        }

        void SaveGameMenuEntry_Selected(object sende, PlayerIndexEventArgs e)
        {
            // Only allow saving after purchase to prevent playthrough through repeated savings
            if (Guide.IsTrialMode)
            {
                SignedInGamer gamer = Gamer.SignedInGamers[(PlayerIndex)this.ControllingPlayer];

                if (gamer != null)
                {
                    if (gamer.Privileges.AllowPurchaseContent)
                    {
                        Guide.ShowMarketplace((PlayerIndex)this.ControllingPlayer);
                    }
                    else
                    {
                        MessageBoxScreen useAllowContentBox = new MessageBoxScreen(new string[] { "Please use an account with purchase", "privileges to save the game!" }, true, false, true, true);
                        useAllowContentBox.Cancelled += OnCancel;
                        useAllowContentBox.Accepted += OnCancel;
                        ScreenManager.AddScreen(useAllowContentBox, this.ControllingPlayer.Value);
                    }
                }
                else
                {
                    //sign in and purchase message box
                    MessageBoxScreen signInBox = new MessageBoxScreen(new string[] { "Please sign in to an account with purchase", "privileges to save the game!" }, true, false, true, true);
                    signInBox.Cancelled += OnCancel;
                    signInBox.Accepted += OnCancel;
                    ScreenManager.AddScreen(signInBox, this.ControllingPlayer.Value);
                }

                return;
            }
            else
            {
                SignedInGamer gamer = Gamer.SignedInGamers[(PlayerIndex)this.ControllingPlayer];

                if (gamer != null)
                {
                    if (!gamer.IsGuest)
                    {
                    }
                    else
                    {
                        //sign in and purchase message box
                        MessageBoxScreen signInBox = new MessageBoxScreen(new string[] { "Please sign in to a non-guest account", "to save the game!" }, true, false, true, true);
                        signInBox.Cancelled += OnCancel;
                        signInBox.Accepted += OnCancel;
                        ScreenManager.AddScreen(signInBox, this.ControllingPlayer.Value);

                        return;
                    }
                }
                else
                {
                    //sign in and purchase message box
                    MessageBoxScreen signInBox = new MessageBoxScreen(new string[] { "Please sign in to a non-guest account", "to save the game!" }, true, false, true, true);
                    signInBox.Cancelled += OnCancel;
                    signInBox.Accepted += OnCancel;
                    ScreenManager.AddScreen(signInBox, this.ControllingPlayer.Value);

                    return;
                }
            }

            string fileName = "SaveGame";
            RenderTarget2D renderTarget;

            // make sure the device is ready
            try
            {
                if (this.ScreenManager.SaveDevice.IsReady)
                {

                    // Show box
                    MessageBoxScreen currentSaveMessageBox = new MessageBoxScreen(new string[] { "Saving, please wait..." }, false, false, false, false);
                    currentSaveMessageBox.Cancelled -= OnCancel;
                    currentSaveMessageBox.Accepted -= OnCancel;
                    ScreenManager.AddScreen(currentSaveMessageBox, this.ControllingPlayer.Value);

                    // Get max file count
                    string[] filenames = this.ScreenManager.SaveDevice.GetFiles("MurderForDinner");
                    List<int> numbersUsed = new List<int>();
                    for (int i = 0; i < filenames.Length; i++)
                    {
                        numbersUsed.Add(Convert.ToInt32(filenames[i].Substring(4), CultureInfo.InvariantCulture));
                    }
                    numbersUsed.Sort();
                    int max = (numbersUsed.Count > 0 ? numbersUsed[numbersUsed.Count - 1] : 0);

                    // Save file
                    fileName = "Save" + (max + 1).ToString();

                    renderTarget = new RenderTarget2D(this.ScreenManager.GraphicsDevice, 480, 270, false, SurfaceFormat.Color, DepthFormat.Depth16);
                    this.ScreenManager.GraphicsDevice.SetRenderTarget(renderTarget);
                    world.Draw(new GameTime());
                    this.ScreenManager.GraphicsDevice.SetRenderTarget(null);

                    if (this.ScreenManager.SaveDevice.IsReady)
                    {
                        // save a file asynchronously. this will trigger IsBusy to return true
                        // for the duration of the save process.
                        this.ScreenManager.SaveDevice.SaveAsync(
                            "MurderForDinner",
                            fileName,
                            stream =>
                            {
                                try
                                {
                                    uint[] colorData = new uint[renderTarget.Width * renderTarget.Height];
                                    renderTarget.GetData<uint>(colorData);

                                    using (BinaryWriter writer = new BinaryWriter(stream))
                                    {
                                        // Write the screenshot
                                        writer.Write(colorData.Length);
                                        for (int i = 0; i < colorData.Length; i++)
                                        {
                                            writer.Write(colorData[i]);
                                        }
                                        writer.Write(GlobalVariables.loadVariables.level);                // Write which XML file we are using
                                        writer.Write((double)world.CharacterPosition.X);        // Write the character position
                                        writer.Write((double)world.CharacterPosition.Y);
                                        writer.Write((double)world.CharacterPosition.Z);
                                        writer.Write((double)world.CharacterOrientation.X);     // Write the character orientation
                                        writer.Write((double)world.CharacterOrientation.Y);
                                        writer.Write((double)world.CharacterOrientation.Z);
                                        writer.Write((double)world.CharacterOrientation.W);
                                        writer.Write((double)world.CameraPitch);                // Write the camera pitch
                                        writer.Write((double)world.CameraYaw);                  // Write the camera yaw
                                        writer.Write((double)world.TotalRotationY);             // Write total y rotation for character
                                        writer.Write(world.TimePlayed);                         // Write the time played in milliseconds
                                        //writer.Write(DateTime.Now.ToBinary());                  // Write the save time
                                        writer.Write(DateTime.Now.ToFileTime());
                                        writer.Write(Convert.ToInt32(GlobalVariables.loadVariables.gameState.Count, CultureInfo.InvariantCulture));         // Write the number of states we are saving
                                        foreach (KeyValuePair<string, string> k in GlobalVariables.loadVariables.gameState)
                                        {
                                            writer.Write(k.Key);                                // Write the key
                                            writer.Write(k.Value);                              // Write the value
                                        }

                                        // Track which models have changed positioning info
                                        // Levels and model names combined are unique, so just add here.
                                        Dictionary<string, Dictionary<string, ModelVariables>> movedModels = new Dictionary<string, Dictionary<string, ModelVariables>>();

                                        foreach (KeyValuePair<string, StaticModel> k in world.levelManager.StaticModels)
                                        {
                                            if (k.Value.HasMoved)
                                            {
                                                if (!movedModels.ContainsKey(GlobalVariables.loadVariables.level))
                                                {
                                                    movedModels.Add(GlobalVariables.loadVariables.level, new Dictionary<string, ModelVariables>());
                                                }

                                                movedModels[GlobalVariables.loadVariables.level].Add(k.Key, new ModelVariables(k.Value.Position, k.Value.Rotation, k.Value.Scale));
                                            }
                                        }
                                        foreach (KeyValuePair<string, DynamicModel> k in world.levelManager.DynamicModels)
                                        {
                                            if (k.Value.HasMoved)
                                            {
                                                if (!movedModels.ContainsKey(GlobalVariables.loadVariables.level))
                                                {
                                                    movedModels.Add(GlobalVariables.loadVariables.level, new Dictionary<string, ModelVariables>());
                                                }

                                                movedModels[GlobalVariables.loadVariables.level].Add(k.Key, new ModelVariables(k.Value.Position, k.Value.Rotation, k.Value.Scale));
                                            }
                                        }
                                        foreach (KeyValuePair<string, AnimatedModel> k in world.levelManager.AnimatedModels)
                                        {
                                            if (k.Value.HasMoved)
                                            {
                                                if (!movedModels.ContainsKey(GlobalVariables.loadVariables.level))
                                                {
                                                    movedModels.Add(GlobalVariables.loadVariables.level, new Dictionary<string, ModelVariables>());
                                                }

                                                movedModels[GlobalVariables.loadVariables.level].Add(k.Key, new ModelVariables(k.Value.Position, k.Value.Rotation, k.Value.Scale));
                                            }
                                        }

                                        // Also, track our existing moved model data while preventing duplicates and using the latest data (moved models takes precedence over load variables)
                                        foreach (KeyValuePair<string, Dictionary<string, ModelVariables>> level in GlobalVariables.loadVariables.modelPositioning)
                                        {
                                            // Add level name if we don't already have it
                                            if (!movedModels.ContainsKey(level.Key))
                                            {
                                                movedModels.Add(level.Key, new Dictionary<string, ModelVariables>());
                                            }

                                            foreach (KeyValuePair<string, ModelVariables> model in GlobalVariables.loadVariables.modelPositioning[level.Key])
                                            {
                                                // Add model variables if we don't already have it
                                                if (!movedModels[level.Key].ContainsKey(model.Key))
                                                {
                                                    movedModels[level.Key].Add(model.Key, new ModelVariables(model.Value.position, model.Value.rotation, model.Value.scale));
                                                }
                                            }
                                        }

                                        // Now save all our moved model data
                                        Int32 numberOfMovedModels = 0;
                                        foreach (KeyValuePair<string, Dictionary<string, ModelVariables>> level in movedModels)
                                        {
                                            foreach (KeyValuePair<string, ModelVariables> model in movedModels[level.Key])
                                            {
                                                numberOfMovedModels++;
                                            }
                                        }

                                        writer.Write(numberOfMovedModels);         // Write the number of states we are saving
                                        foreach (KeyValuePair<string, Dictionary<string, ModelVariables>> level in movedModels)
                                        {
                                            foreach (KeyValuePair<string, ModelVariables> model in movedModels[level.Key])
                                            {
                                                writer.Write(level.Key);
                                                writer.Write(model.Key);
                                                writer.Write(model.Value.position.X + "," + model.Value.position.Y + "," + model.Value.position.Z + ",");
                                                writer.Write(model.Value.rotation.X + "," + model.Value.rotation.Y + "," + model.Value.rotation.Z + ",");
                                                writer.Write(model.Value.scale.X + "," + model.Value.scale.Y + "," + model.Value.scale.Z + ",");
                                            }
                                        }
                                    }
                                }
                                catch
                                {
                                    // Remove topmost screen ("Saving file, please wait...")
                                    ScreenManager.RemoveScreenTopmost();

                                    MessageBoxScreen confirmExitMessageBox = new MessageBoxScreen(new string[] { "Could not save file. Please retry." }, true, false, true, true);
                                    confirmExitMessageBox.Accepted += OnCancel;
                                    ScreenManager.AddScreen(confirmExitMessageBox, this.ControllingPlayer.Value);
                                }
                            });
                    }
                    else
                    {
                        MessageBoxScreen warningMessageBox = new MessageBoxScreen(new string[] { "Save device is not ready. Please retry." }, true, false, true, true);
                        warningMessageBox.Accepted += OnCancel;
                        ScreenManager.AddScreen(warningMessageBox, this.ControllingPlayer.Value);
                    }
                }//SaveDevice.IsReady
                else
                {
                    MessageBoxScreen warningMessageBox = new MessageBoxScreen(new string[] { "Save device is not ready. Please retry." }, true, false, true, true);
                    warningMessageBox.Accepted += OnCancel;
                    ScreenManager.AddScreen(warningMessageBox, this.ControllingPlayer.Value);
                }
            }
            catch
            {
                // Remove topmost screen ("Saving file, please wait...")
                ScreenManager.RemoveScreenTopmost();

                MessageBoxScreen confirmExitMessageBox = new MessageBoxScreen(new string[] { "Could not read drive. Please retry." }, true, false, true, true);
                confirmExitMessageBox.Accepted += OnCancel;
                ScreenManager.AddScreen(confirmExitMessageBox, this.ControllingPlayer.Value);

                return;
            }
        }

        /// <summary>
        /// Event handler for when the user selects ok on the "are you sure
        /// you want to quit" message box. This uses the loading screen to
        /// transition from the game back to the main menu screen.
        /// </summary>
        void ConfirmQuitMessageBoxAccepted(object sender, PlayerIndexEventArgs e)
        {
            //LoadingScreen.Load(ScreenManager, false, null, new BackgroundScreen(),
            //                                               new MainMenuScreen());
            LoadingScreen.Load(ScreenManager, false, null, new BackgroundScreen(), new StartScreen());
        }

        void SaveDevice_SaveCompleted(object sender, FileActionCompletedEventArgs args)
        {
            this.ScreenManager.SaveDevice.SaveCompleted -= SaveDevice_SaveCompleted;

            // Remove topmost screen
            ScreenManager.RemoveScreenTopmost();

            if (args.Error == null)
            {
                // Saved successfully
                MessageBoxScreen warningMessageBox = new MessageBoxScreen(new string[] { "File saved!" }, true, false, true, true);
                warningMessageBox.Cancelled += OnCancel;
                warningMessageBox.Accepted += OnCancel;
                ScreenManager.AddScreen(warningMessageBox, this.ControllingPlayer.Value);
            }
            else
            {
                // Errored
                MessageBoxScreen warningMessageBox = new MessageBoxScreen(new string[] { "Error saving file. ", args.Error.Message }, true, false, true, true);
                warningMessageBox.Cancelled += OnCancel;
                warningMessageBox.Accepted += OnCancel;
                ScreenManager.AddScreen(warningMessageBox, this.ControllingPlayer.Value);
            }
        }

        #endregion
    }
}
